package com.binance.binancedemo.recycler;


import com.binance.binancedemo.utils.L;

import org.reactivestreams.Subscription;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import io.reactivex.disposables.Disposable;
import io.reactivex.exceptions.Exceptions;

/**
 */

public class WeakCompositeSubscription implements Disposable {
    private Set<WeakReference<Disposable>> disposables;
    private volatile boolean disposed;

    public WeakCompositeSubscription() {
    }

    /**
     * Adds a new {@link Subscription} to this {@code CompositeSubscription} if the
     * {@code CompositeSubscription} is not yet unsubscribed. If the {@code CompositeSubscription} <em>is</em>
     * unsubscribed, {@code add} will indicate this by explicitly unsubscribing the new {@code Subscription} as
     * well.
     *
     * @param s
     *          the {@link Subscription} to add
     */
    public void add(final Disposable s) {
        if (s.isDisposed()) {
            return;
        }
        if (!disposed) {
            synchronized (this) {
                if (!disposed) {
                    if (disposables == null) {
                        disposables = new HashSet<WeakReference<Disposable>>(4);
                    }
                    disposables.add(new WeakReference<Disposable>(s));
                    return;
                }
            }
        }
        // call after leaving the synchronized block so we're not holding a lock while executing this
        s.dispose();
    }

    /**
     * Unsubscribes any disposables that are currently part of this {@code CompositeSubscription} and remove
     * them from the {@code CompositeSubscription} so that the {@code CompositeSubscription} is empty and
     * able to manage new disposables.
     */
    public void clear() {
        if (!disposed) {
            Collection<WeakReference<Disposable>> unsubscribe = null;
            synchronized (this) {
                if (disposed || disposables == null) {
                    return;
                } else {
                    unsubscribe = disposables;
                    disposables = null;
                }
            }
            disposedFromAll(unsubscribe);
        }
    }

    /**
     * Unsubscribes itself and all inner disposables.
     * will be unsubscribed immediately.
     */
    @Override
    public void dispose() {
        if (!disposed) {
            Collection<WeakReference<Disposable>> unsubscribe = null;
            synchronized (this) {
                if (disposed) {
                    return;
                }
                disposed = true;
                unsubscribe = disposables;
                disposables = null;
            }
            // we will only get here once
            disposedFromAll(unsubscribe);
        }
    }

    private static void disposedFromAll(Collection<WeakReference<Disposable>> subscriptions) {
        if (subscriptions == null) {
            return;
        }
        List<Throwable> es = null;
        for (WeakReference<Disposable> ref : subscriptions) {
            try {
                Disposable s = (ref!=null && ref.get()!=null)?ref.get():null;
                L.i("dispose:"+(s==null?"null":s));
                if(s!=null) {
                    s.dispose();
                }
            } catch (Throwable e) {
                if (es == null) {
                    es = new ArrayList<Throwable>();
                }
                es.add(e);
            }
        }
        if(es!=null && !es.isEmpty()){
            Exceptions.throwIfFatal(es.get(0));
        }
    }

    /**
     * Returns true if this composite is not unsubscribed and contains disposables.
     *
     * @return {@code true} if this composite is not unsubscribed and contains disposables.
     * @since 1.0.7
     */
    public boolean hasDisposes() {
        if (!disposed) {
            synchronized (this) {
                return !disposed && disposables != null && !disposables.isEmpty();
            }
        }
        return false;
    }


    @Override
    public boolean isDisposed() {
        return disposed;
    }
}
